﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Web;
using System.IO;
using Microsoft.SharePoint;

namespace SPHelper
{
	public class Utilities
	{
		public static string Version { get { return "1.2"; } }

		public static int RandomNumber(int minNumber, int maxNumber)
		{
			Random random = new Random();
			if (maxNumber <= minNumber) { maxNumber = minNumber + 2; }
			return random.Next(minNumber, (maxNumber + 1));
		}
		public static int RandomNumber()
		{
			return RandomNumber(100000, 999999); 
		}


		/// <summary>
		/// Get time duration from start datetime representing the start of the duration
		/// Run this at the end of the duration
		/// </summary>
		/// <param name="start"></param>
		/// <returns></returns>
		public static TimeSpan GetTimeSpan(DateTime start)
		{
			if (start != null && start != DateTime.MinValue)
			{
				return DateTime.Now.Subtract(start);
			}
			else { return new TimeSpan(); }
		}

		/// <summary>
		/// convert passed timespan into a human readable string
		/// </summary>
		/// <param name="span"></param>
		/// <returns></returns>
		public static string ConvertTimeSpanToString(TimeSpan span)
		{
			string time = null;

			// hours
			if (span.Hours > 0) { time += span.Hours + "h "; }
			// minutes
			if (!string.IsNullOrEmpty(time) || span.Minutes > 0) { time += span.Minutes + "m "; }
			// seconds
			if (!string.IsNullOrEmpty(time) || span.Seconds > 0) { time += span.Seconds + "s "; }
			// miliseconds (show regardless)
			time += span.Milliseconds + "ms";

			return time;
		}

		/// <summary>
		/// Show time duration. At beginning of code, set variable to DateTime.Now. At end of code, run this function
		/// </summary>
		/// <param name="start"></param>
		/// <returns></returns>
		public static string GetTimeSpanAsString(DateTime start)
		{
			if (start != null && start != DateTime.MinValue)
			{
				return ConvertTimeSpanToString(GetTimeSpan(start));
			}
			else { return null; }
		}

		public static bool IsEvenNumber(int number)
		{
			if (number % 2 == 0) { return true; }
			else { return false; }
		}

		public static bool IsOddNumber(int number)
		{
			return !IsEvenNumber(number);
		}

		/// <summary>
		/// Convert a stream object into memory stream object
		/// </summary>
		/// <param name="stream"></param>
		/// <returns></returns>
		public static MemoryStream ConvertStreamToMemoryStream(Stream stream)
		{
			MemoryStream memoryStream = null;
			if (stream != null && stream != Stream.Null)
			{
				memoryStream = new MemoryStream();
				byte[] buffer = new byte[1024];
				int byteCount;
				do
				{
					byteCount = stream.Read(buffer, 0, buffer.Length);
					memoryStream.Write(buffer, 0, byteCount);
				} 
				while (byteCount > 0);
			}
			return memoryStream;
		}

		public static DateTime GetDateFromLastWeek(DayOfWeek dayOfWeek)
		{
			// get current date
			DateTime dateToReturn = DateTime.Now;

			// subtract days to get to saturday of last week (last day of last week)
			dateToReturn = dateToReturn.AddDays(-((int)dateToReturn.DayOfWeek + 1));

			// now loop through last week to get day passed
			while (dateToReturn.DayOfWeek != dayOfWeek)
			{
				dateToReturn = dateToReturn.AddDays(-1);
			}
			return dateToReturn;
		}

		/// <summary>
		/// return a string providing the execution duration
		/// </summary>
		/// <param name="startExecuteDateTime"></param>
		/// <returns></returns>
		public static string executeDuration(DateTime startExecuteDateTime)
		{
			TimeSpan span = DateTime.Now - startExecuteDateTime;
			string o = string.Empty;
			if (span.TotalDays > 0) { o += span.Days + "d "; }
			if (span.TotalHours > 0) { o += span.Hours + "h "; }
			if (span.TotalMinutes > 0) { o += span.Minutes + "m "; }
			if (span.TotalSeconds > 0) { o += span.Seconds + "s "; }
			if (span.TotalMilliseconds > 0) { o += span.Milliseconds + "ms "; }
			else { o = "0ms"; }

			return o;
		}


		/// <summary>
		/// Convert passed string into title case
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		public static string ToTitleCase(string input)
		{
			// important: must set to lower, if any char is uppercase, then whole string is ignored
			input = input.ToLower();
			input = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input);
			return input;
		}

		/// <summary>
		/// Determine if passed url is an absolute url with "http://" or "ftp://" prefix
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		public static bool IsValidAbsoluteUrl(string url, bool isThrowError)
		{
			bool isValid = false;
			try
			{
				// init
				if (string.IsNullOrEmpty(url)) { throw new Exception("URL passed is null"); }


				// validate
				if (url.Length < 7)
				{
					throw new Exception("Invalid Url: Too Short");
				}
				if (url.Substring(0, 7).ToLower() != "http://" && url.Substring(0, 6).ToLower() != "ftp://")
				{
					throw new Exception("Invalid Url: Url Must start with a 'http://' or 'ftp://'");
				}
				if (!url.Contains("."))
				{
					throw new Exception("Invalid Url: Missing a dot");
				}
				// at this point if falls through, then valid
				isValid = true;

			}
			catch (Exception ex)
			{
				if (isThrowError)
				{
					Log.WriteError("64urj6trhssersereatraet", ex, url);
				}
				isValid = false;
			}
			return isValid;
		}

		/// <summary>
		/// Simple URL encode
		/// Only encodes non alphanumeric symbols, such as "/" or "?"
		/// </summary>
		/// <param name="stringToEncode"></param>
		/// <returns></returns>
		public static string UrlEncodeSimple(string stringToEncode)
		{
			try
			{
				if (!string.IsNullOrEmpty(stringToEncode))
				{
					stringToEncode = stringToEncode.Replace("/", "%2F");
					stringToEncode = stringToEncode.Replace(":", "%3A");
					stringToEncode = stringToEncode.Replace("=", "%3D");
					stringToEncode = stringToEncode.Replace("&", "%26");
					stringToEncode = stringToEncode.Replace("?", "%3F");
					stringToEncode = stringToEncode.Replace("+", "%2B");
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("6rtshhtGreer54", ex, "String to Encode: " + stringToEncode);
				stringToEncode = null;
			}
			return stringToEncode;
		}
		/// <summary>
		/// Decode passed string, only decodes non alphanumeric chars url encoded
		/// </summary>
		/// <param name="stringToDecode"></param>
		/// <returns></returns>
		public static string UrlDecodeSimple(string stringToDecode)
		{
			try
			{
				if (!string.IsNullOrEmpty(stringToDecode))
				{
					stringToDecode = stringToDecode.Replace("%2F", "/");
					stringToDecode = stringToDecode.Replace("%3A", ":");
					stringToDecode = stringToDecode.Replace("%3D", "=");
					stringToDecode = stringToDecode.Replace("%26", "&");
					stringToDecode = stringToDecode.Replace("%3F", "?");
					stringToDecode = stringToDecode.Replace("%2B", "+");
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("r5rjdtdtsrdrdsrst", ex, "String to Decode: " + stringToDecode);
				stringToDecode = null;
			}
			return stringToDecode;
		}


		/// <summary>
		/// strip out html tags and encoded chars from passed text
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		public static string RemoveHtmlTags(string input)
		{
			try
			{
				// these are end tags that create a line break and need to be replaced with a space
				string[] lineBreakEndTags = new string[] { "</p>", "<br/>", "<br />", "</div>", "</table>", "<nl/>", "<nl />" };

				if (!string.IsNullOrEmpty(input))
				{
					// replace carriage return w/ space
					input = input.Replace("\r", " ");

					// replace newline w/ space
					input = input.Replace("\n", " ");

					// replace tab
					input = input.Replace("\t", string.Empty);

					// replace line break tags with a space
					if (lineBreakEndTags != null)
					{
						foreach (string tag in lineBreakEndTags) { input = input.Replace(tag, " "); }
					}

					// replace all html tags
					Regex regex = new Regex("<(.|\n)*?>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
					input = regex.Replace(input, string.Empty);

					// replace special html characters
					Dictionary<string, string> specialChars = new Dictionary<string, string>();
					specialChars.Add("\u00A0", " ");	// unicode non breaking space
					specialChars.Add(@"&nbsp;", " ");
					specialChars.Add(@"&quot;", "\"");
					specialChars.Add(@"&ldquo;", "\"");
					specialChars.Add(@"&rdquo;", "\"");
					specialChars.Add(@"&rsquo;", "'");
					specialChars.Add(@"&lsquo;", "'");
					specialChars.Add(@"&amp;", "&");
					specialChars.Add(@"&ndash;", "-");
					specialChars.Add(@"&mdash;", "-");
					specialChars.Add(@"&lt;", "<");
					specialChars.Add(@"&gt;", ">");
					specialChars.Add(@"&lsaquo;", "<");
					specialChars.Add(@"&rsaquo;", ">");
					specialChars.Add(@"&trade;", "(tm)");
					specialChars.Add(@"&frasl;", "/");
					specialChars.Add(@"&copy;", "(c)");
					specialChars.Add(@"&reg;", "(r)");
					specialChars.Add(@"&iquest;", "?");
					specialChars.Add(@"&iexcl;", "!");
					specialChars.Add(@"&bull;", "*");

					foreach (KeyValuePair<string, string> specialChar in specialChars)
					{
						input = input.Replace(specialChar.Key, specialChar.Value);
					}

					// any other special char is deleted
					input = System.Text.RegularExpressions.Regex.Replace(input, @"&#[^ ;]+;", string.Empty);
					input = System.Text.RegularExpressions.Regex.Replace(input, @"&[^ ;]+;", string.Empty);

					// remove extra duplicate spaces
					input = System.Text.RegularExpressions.Regex.Replace(input, @"( )+", " ");

					// trim
					if (input != null) { input = input.Trim(); }
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("zsdgsfthsaeaest", ex, "string that failed to be stripped of html: " + input);
			}
			return input;
		}


		/// <summary>
		/// trim and truncate passed text
		/// </summary>
		/// <param name="fieldValue"></param>
		/// <param name="maxLength">0 returns string.Empty</param>
		/// <returns></returns>
		public static string Truncate(string fieldValue, int maxLength)
		{
			return Truncate(fieldValue, maxLength, "...");
		}
		/// <summary>
		/// trim and truncate passed text
		/// 0 = retuns null
		/// </summary>
		/// <param name="fieldValue"></param>
		/// <param name="maxLength"></param>
		/// <param name="trailingString"></param>
		/// <returns></returns>
		public static string Truncate(string fieldValue, int maxLength, string trailingString)
		{
			try
			{
				if (!string.IsNullOrEmpty(fieldValue))
				{
					fieldValue = fieldValue.Trim();
					if (fieldValue.Length > maxLength)
					{
						if (maxLength == 0) { fieldValue = string.Empty; }
						else if (maxLength > 10)
						{
							int trailingDotsLength = 0;
							if (!string.IsNullOrEmpty(trailingString)) { trailingDotsLength = trailingString.Length + 1; }
							fieldValue = fieldValue.Substring(0, (maxLength - trailingDotsLength));
							fieldValue += trailingString;
						}
						else { fieldValue = fieldValue.Substring(0, maxLength); }
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("ewh56rej5", ex, "FieldValue: " + fieldValue + " MaxLength: " + maxLength);
			}
			return fieldValue;
		}

		/// <summary>
		/// truncate text at nearest word boundary (trims whitespace as well)
		/// </summary>
		/// <param name="content"></param>
		/// <param name="maxLength">Will truncate if content passes exceeds this maximum length</param>
		/// <param name="minLength">If defined, will ensure that truncated content does not get smaller than the minimum defined.
		/// For example, the passed content might be all word boundaries, therefore all of it would be truncated, unless min length defined</param>
		/// <param name="wordBoundaries"></param>
		/// <param name="trailingString"></param>
		/// <returns></returns>
		public static string TruncateOnWordBoundary(string content, int maxLength, int? minLength, char[] wordBoundaries,
			string trailingString)
		{
			try
			{
				if (!string.IsNullOrEmpty(content) && content.Length > maxLength)
				{
					// set max width
					if (maxLength > 0 && !string.IsNullOrEmpty(trailingString))
					{
						maxLength = maxLength - trailingString.Length;
					}

					if (maxLength > 0)
					{
						// trim
						content = content.Trim();

						// truncate using maxlength
						content = content.Substring(0, maxLength);

						// trim again
						content = content.Trim();

						if (wordBoundaries != null && wordBoundaries.Length > 0 && !string.IsNullOrEmpty(content))
						{
							int lastIndex = content.LastIndexOfAny(wordBoundaries);
							// only truncate if word boundary found AND word bondary is greater than/equal to min width
							if (lastIndex != -1 && (minLength == null || lastIndex >= minLength.Value))
							{
								// truncate at word border
								content = content.Substring(0, lastIndex);

								// trim again
								content = content.Trim();

								// now make sure no other trailing boundaries, like a comma, dash, etc.
								int inifiteLoopDetection = 1000000;
								for (int i = 0; i < inifiteLoopDetection; i++)
								{
									// stop if content now empty (word was ALL word boundaries) or content at min length
									if (content.Length == 0 || (minLength != null && content.Length <= minLength.Value)) { break; }

									if (content.Substring(content.Length - 1).LastIndexOfAny(wordBoundaries) != -1)
									{
										// last char is a word boundary
										content = content.Substring(0, content.Length - 1).Trim();
									}
									else
									{
										break;
									}
								} // loop to find dangling broundies

							} // word boundary match

						} // boundaries defined

					} // max length greater than 0

					// append trailing string
					if (!string.IsNullOrEmpty(content) && !string.IsNullOrEmpty(trailingString))
					{
						content += trailingString;
					}

				} // content not null/empty 
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("t6u5itkudthdsfsdfdrg", ex);
			}
			return content;
		}

		public static char[] WordBoundaries = new char[] { ' ', ',', '.', '-', ':', ';', '?', '!' };

		/// <summary>
		/// truncate on word boundary (trims whitespace as well)
		/// </summary>
		/// <param name="content"></param>
		/// <param name="maxLength"></param>
		/// <returns></returns>
		public static string TruncateOnWordBoundary(string content, int maxLength)
		{
			return TruncateOnWordBoundary(content, maxLength, null, WordBoundaries, null);
		}


		/// <summary>
		/// Truncate on word boundary (trims as well)
		/// </summary>
		/// <param name="content">content to truncate</param>
		/// <param name="maxLength">max length allowed</param>
		/// <param name="trailingString">trailing dots or other type of content to add ONLY if truncate occurred</param>
		/// <returns></returns>
		public static string TruncateOnWordBoundary(string content, int maxLength, string trailingString)
		{
			return TruncateOnWordBoundary(content, maxLength, null, WordBoundaries, trailingString);
		}


		/// <summary>
		/// Turn on or off the SharePoint developer dashboard
		/// http://www.sharepoint4arabs.com/AymanElHattab/Lists/Posts/Post.aspx?List=0b2c413e-e6fb-4c60-a12f-9ae9bdbd48c8&ID=173
		/// </summary>
		/// <param name="isTurnOn"></param>
		public static void SetDeveloperDashboardStatus(bool isTurnOn)
		{
			try
			{
				Microsoft.SharePoint.Administration.SPWebService contentService = Microsoft.SharePoint.Administration.SPWebService.ContentService;
				Microsoft.SharePoint.Administration.SPDeveloperDashboardSettings developerDashboard =
					contentService.DeveloperDashboardSettings;
				if (isTurnOn)
				{
					developerDashboard.DisplayLevel = Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel.On;
				}
				else
				{
					developerDashboard.DisplayLevel = Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel.On;
				}

				// update
				developerDashboard.Update(); 
			}
			catch (Exception ex)
			{
				Log.WriteError("dsrtgjutfe5fdxcxg", ex);
			}
		}

		/// <summary>
		/// remove the trailing seperator for a string
		/// For example, RemoveLastSeparator("item1, item2, item3,",",")
		/// will remove the last ","
		/// </summary>
		/// <param name="text"></param>
		/// <param name="separator"></param>
		/// <returns></returns>
		public static string RemoveLastSeparator(string text, string separator)
		{
			try
			{
				if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(separator)
				&& text.Substring(text.Length - separator.Length, separator.Length) == separator)
				{
					text = text.Substring(0, (text.Length - separator.Length));
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("dgrfgtr6r6drtdxf", ex, "text: " + text);
			}
			return text;
		}


		/// <summary>
		/// Clean up url, useful for url tracking, etc.
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		public static string CleanUrl(string url, bool isMakeRelativeUrl)
		{
			string urlOriginal = url;
			try
			{
				if (url == string.Empty) { url = "/"; }

				if (!string.IsNullOrEmpty(url))
				{
					// init
					string[] urlSegmented = url.Split('/');

					////////////////////////////////////////////////////////////////////
					// remove domain info
					if (isMakeRelativeUrl && url.Contains("://"))
					{
						url = string.Empty;
						int i = 0;
						foreach (string s in urlSegmented)
						{
							if (i > 2) { url += "/" + s; }
							i++;
						}
					}

					// Log.WriteInformation("url: " + url);

					//////////////////////////////////////////////////////////////////////
					//// remove default.aspx
					//if (isRemoveDefaultAspx && url.ToLower().Contains("default.aspx"))
					//{
					//    url = url.Replace("default.aspx", "").Replace("Default.aspx", "");
					//}
					//// Log.WriteInformation("url: " + url);


					////////////////////////////////////////////////////////////////////
					// remove unncesseary url params
					
					// a.) remove trailing question mark if nothing after it
					if (url.Substring(url.Length-1, 1) == "?")
					{
						url = url.Substring(0, url.Length-1);
					}
					// Log.WriteInformation("url: " + url);

					if (url.Contains("?"))
					{
						// b.) get just the params after the "?"
						string urlParamsText = url.Substring(url.IndexOf("?") + 1, url.Length - url.IndexOf("?") - 1);
						url = url.Substring(0, url.IndexOf("?"));
						// Log.WriteInformation("url: " + url + " urlParams: " + urlParamsText);

						// c.) segment url params
						string[] urlParamsSegmented = urlParamsText.Split('&');
						urlParamsText = string.Empty;

						// d.) loop through params, remove those unnecessary
						string[] paramsToRemove = new string[] 
						{ 
							"page",
							"isprint", 
							"isclearcache", 
							"authoringerror", 
							"loginasanotheruser",
							"returnurl",
							"controlmode",
							"displaymode",
							"utm_source",
							"utm_campaign",
							"utm_medium"
						};
						foreach (string urlParamNameValue in urlParamsSegmented)
						{
							bool isMatch = false;
							foreach (string paramToRemove in paramsToRemove)
							{
								if (urlParamNameValue.ToLower().Contains(paramToRemove + "="))
								{
									isMatch = true;
									break;
								}
							}
							if (!isMatch)
							{
								// valid param, add to url
								urlParamsText += "&" + urlParamNameValue;
							}
						}
						if (urlParamsText.Length > 0 && urlParamsText.Substring(0, 1) == "&")
						{ urlParamsText = urlParamsText.Substring(1, urlParamsText.Length - 1); }
						// Log.WriteInformation("url params: " + urlParamsText);

						// e.) add url + params back together
						if (!string.IsNullOrEmpty(urlParamsText))
						{
							if (url.Substring(url.Length-1,1) != "?")
							{
								url += "?";
							}
							url += urlParamsText;
						}
						// Log.WriteInformation("url: " + url);
					}

					////////////////////////////////////////////////////////////////////
					// Final clean up url
					urlSegmented = url.Split('/');
					
					// a.) add trailing slash if needed
					if (!url.Contains("?") && !url.Contains("&") && 
						urlSegmented.Length > 0 && !urlSegmented[urlSegmented.Length-1].Contains(".") &&
						url.Substring(url.Length-1, 1) != "/")
					{
						url += "/";
					}
					if (string.IsNullOrEmpty(url)) { url = "/"; }
					// Log.WriteInformation("url: " + url);

					// b.) remove trailing "&"
					if (url.Substring(url.Length-1, 1) == "&")
					{
						url = url.Substring(0, url.Length-1);
					}
					// Log.WriteInformation("url: " + url);

					// c.) remove trailing "?"
					if (url.Substring(url.Length-1, 1) == "?")
					{
						url = url.Substring(0, url.Length-1);
					}
					// Log.WriteInformation("url: " + url);

					// d.) trim
					url = url.Trim();
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("yr67itydrsrdhdsrs", ex, "url: " + url + " original url passed: " + urlOriginal);
				url = urlOriginal;
			}
			return url;
		}

		/// <summary>
		/// Append url variable to passed url
		/// </summary>
		/// <param name="originalUrl">url to append to</param>
		/// <param name="paramName">url param name</param>
		/// <param name="paramValue">url param value</param>
		/// <param name="isEncodeValue">If true, will url encode the param value</param>
		/// <returns></returns>
		public static string AppendUrlParameter(string originalUrl, string paramName, string paramValue, bool isEncodeValue)
		{
			string url = originalUrl;
			try
			{
				if (!string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(paramName))
				{
					// trim
					url = url.Trim();

					// add url param delimiter, "?" or "&"
					if (url.Substring(url.Length - 1) == "?")
					{
						// don't add, already there
					}
					else if (url.Substring(url.Length - 1) == "&")
					{
						// don't add, already there
					}
					else if (url.Contains("?"))
					{
						// url already has "?" and doesn't end in delimiter, therefore add "&"
						url += "&";
					}
					else
					{
						// url doesn't have "?" and doesn't end in delimiter, therefore add "?"
						url += "?";
					}

					// add param name
					url += paramName;

					// encode param value?
					if (isEncodeValue && !string.IsNullOrEmpty(paramValue))
					{
						paramValue = System.Web.HttpUtility.UrlDecode(paramValue);
					}

					// add param value
					if (!string.IsNullOrEmpty(paramValue))
					{
						url += "=" + paramValue;
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("8iy7ut65dfszsadesresr", ex, "original url passed: " + originalUrl);
				url = originalUrl;
			}
			return url;
		}


		/// <summary>
		/// Get IP Address for current session calling this member
		/// </summary>
		/// <returns></returns>
		public static string GetIpAddress()
		{
			string ipAddress = null;
			try
			{
				if (System.Web.HttpContext.Current != null && 
					System.Web.HttpContext.Current.Request != null && 
					System.Web.HttpContext.Current.Request.UserHostAddress != null
					) 
				{ 
					ipAddress = System.Web.HttpContext.Current.Request.UserHostAddress; 
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("ftugfyudf", ex);
				ipAddress = null;
			}
			return ipAddress;
		}


		/// <summary>
		/// Parse passed url, get param value
		/// </summary>
		/// <param name="paramName"></param>
		/// <param name="url"></param>
		/// <returns></returns>
		public static string GetUrlParamValue(string paramName, string url)
		{
			string paramValue = null;
			try
			{
				if (!string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(url)
					&& url.ToLower().Contains(paramName.ToLower().Trim() + "="))
				{
					paramValue = url.ToLower();

					// format param name
					paramName = paramName.ToLower().Trim() + "=";

					// get param value and everthing after it
					paramValue = paramValue.Substring(paramValue.IndexOf(paramName), paramValue.Length - paramValue.IndexOf(paramName));
					paramValue = paramValue.Replace(paramName, "");

					// remove any other params
					if (paramValue.Contains("&")) { paramValue = paramValue.Substring(0, paramValue.IndexOf("&")); }

					// clean up
					paramValue = paramValue.Trim();
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("dsrdfyudtsr5esd5rd6fc", ex, "param name: " + paramName + " param value: "
					+ paramValue + " url: " + url);
				paramValue = null;
			}
			return paramValue;
		}


		/// <summary>
		/// format a view field for an SPQuery, 
		/// for query.ViewFields = FormatSPQueryViewFields(string[]);
		/// </summary>
		/// <param name="fields"></param>
		/// <returns></returns>
		public static string FormatSPQueryViewFields(string[] fields)
		{
			string o = null;
			if (fields != null && fields.Length > 0)
			{
				foreach (string field in fields)
				{
					o += "<FieldRef Name='" + field + "' />";
				}
			}
			return o;
		}

		public static string ConvertArrayToString(string[] stringArray, string separator)
		{
			string o = null;
			if (stringArray != null && stringArray.Length > 0)
			{
				foreach (string s in stringArray)
				{
					o += s + separator;
				}
				o = RemoveLastSeparator(o, separator);
			}
			return o;
		}

		public static string ConvertArrayToString(string[] stringArray)
		{
			return ConvertArrayToString(stringArray, ", ");
		}

		public static string ConvertArrayToString(int[] intArray)
		{
			// 1.) convert int array to string array
			string[] stringArray = null;
			if (intArray != null)
			{
				stringArray = new string[intArray.Length];
				for (int i = 0; i < intArray.Length; i++)
				{
					stringArray[i] = intArray[i].ToString();
				}
			}

			// 2.) call original
			return ConvertArrayToString(stringArray, ", ");
		}

		public static string ConvertListToString(List<string> list, string separator)
		{
			if (list != null && list.Count > 0)
			{
				var stringArray = list.ToArray();
				// call original
				return ConvertArrayToString(stringArray, separator);
			}
			return null;
		}
		public static string ConvertListToString(List<string> list)
		{
			return ConvertListToString(list, ", ");
		}

		public static string ConvertListToString(List<int> list, string separator)
		{
			if (list != null && list.Count > 0)
			{
				var stringList = new List<string>();
				foreach (int i in list)
				{
					stringList.Add(i.ToString());
				}
				return ConvertListToString(stringList, separator);
			}
			return null;
		}
		public static string ConvertListToString(List<int> list)
		{
			return ConvertListToString(list, ", ");
		}


		public static string[] AppendToArray(string[] array, string elementToAppend)
		{
			var list = new List<string>();
			if (array != null) { foreach (string a in array) { list.Add(a); } }
			
			list.Add(elementToAppend);
			return list.ToArray();
		}
		public static int[] AppendToArray(int[] array, int elementToAppend)
		{
			var list = new List<int>();
			if (array != null) { foreach (int a in array) { list.Add(a); } }

			list.Add(elementToAppend);
			return list.ToArray();
		}


		
		/// <summary>
		/// Send an email
		/// </summary>
		/// <param name="from"></param>
		/// <param name="to"></param>
		/// <param name="cc"></param>
		/// <param name="bcc"></param>
		/// <param name="subject"></param>
		/// <param name="isInsertSubjectPrefix"></param>
		/// <param name="body"></param>
		/// <param name="isHtmlFormat"></param>
		/// <param name="isShowLoggingFooter"></param>
		/// <param name="isLogOnError"></param>
		/// <returns></returns>
		public static bool Email(string from, string[] to, string[] cc, string[] bcc, string subject, 
			bool isInsertSubjectPrefix, string body, bool isHtmlFormat, bool isShowLoggingFooter, bool isLogOnError)
		{
			bool isSuccess = false;
			try
			{
				// init
				if (string.IsNullOrEmpty(from)) { from = Constants.EmailDefaultSender; }
				if ((to == null || to.Length == 0) && 
					(cc == null || cc.Length == 0) && 
					(bcc == null || bcc.Length == 0)) 
				{ throw new Exception("No email recipients defined"); }
				if (string.IsNullOrEmpty(subject)) { subject = string.Empty; }
				System.Net.Mail.MailMessage m = new System.Net.Mail.MailMessage();


				// from
				System.Net.Mail.MailAddress fromAddress = new System.Net.Mail.MailAddress(from);
				m.From = fromAddress;

				// to
				if (to != null && to.Length > 0)
				{
					foreach (string _to in to) { if (!string.IsNullOrEmpty(_to)) { m.To.Add(_to); } }
				}

				// cc
				if (cc != null && cc.Length > 0)
				{
					foreach (string _cc in cc) { if (!string.IsNullOrEmpty(_cc)) { m.CC.Add(_cc); } }
				}

				// bcc
				if (bcc != null && bcc.Length > 0)
				{
					foreach (string _bcc in bcc) { if (!string.IsNullOrEmpty(_bcc)) { m.Bcc.Add(_bcc); } }
				}

				// subject
				if (isInsertSubjectPrefix) { subject = Constants.EmailSubjectPrefix + " " + subject.Trim(); }
				subject = subject.Replace('\r', ' ').Replace('\n', ' ');
				if (subject.Length > 240) { subject = subject.Substring(0, 235) + "..."; }
				m.Subject = subject;

				// body
				m.IsBodyHtml = isHtmlFormat;
				if (isShowLoggingFooter)
				{
					string footer = Log.Footer(isHtmlFormat);
					if (!string.IsNullOrEmpty(footer)) { body += footer; }
				}
				m.Body = body;

				//////////////////////////////////////////////////////////////////////
				// send email
				if (!Log.IsDevelopmentEnvironment())
				{
					// send email
					System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient();
					if (client == null) { throw new Exception("Failed to create smpt client object"); }
					client.Timeout = 300000;	// http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.timeout.aspx
					client.Send(m);
					m.Dispose();
					client = null;
				}
				else
				{
					Log.WriteTextLog("Logging instead of emailing since on a dev environment.", "Subject: " + subject + "\r\nBody: " + body);
				}

				// assume success
				isSuccess = true;
			}
			catch (Exception ex)
			{
				if (isLogOnError)
				{
					Log.WriteError("ehge57itrc", ex, "to: " + Utilities.ConvertArrayToString(to)
						+ " subject: " + subject);
				}
				else
				{
					Log.WriteTextLog("Failed to Email", ex.ToString() + "\n\n"
						+ "\nto: " + Utilities.ConvertArrayToString(to)
						+ "\ncc: " + Utilities.ConvertArrayToString(cc)
						+ "\nbcc: " + Utilities.ConvertArrayToString(bcc) 
						+ "\nsubject: " + subject
						+ "\nBody:\n" + body);
				}
				isSuccess = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Email
		/// </summary>
		/// <param name="from">who to send from</param>
		/// <param name="to">Can use semicolon, colon separators</param>
		/// <param name="cc">Can use semicolon, colon separators</param>
		/// <param name="bcc">Can use semicolon, colon separators</param>
		/// <param name="subject">Automatically prefixes subject</param>
		/// <param name="body"></param>
		/// <param name="isHtmlFormat"></param>
		/// <returns></returns>
		public static bool Email(string from, string to, string cc, string bcc, string subject, string body, bool isHtmlFormat)
		{
			string[] _to = ParseEmails(to);
			string[] _cc = ParseEmails(cc);
			string[] _bcc = ParseEmails(bcc);

			return Email(from, _to, _cc, _bcc, subject, true, body, isHtmlFormat, false, true);
		}

		/// <summary>
		/// Parse string of emails, using semicolon, comma, space etc separators
		/// </summary>
		/// <param name="emails"></param>
		/// <returns></returns>
		public static string[] ParseEmails(string emails)
		{
			string[] emailArry = null;
			if (!string.IsNullOrEmpty(emails))
			{
				char[] separators = new char[] { ';', ',', ' ' };

				foreach (char s in separators)
				{
					// split emails using char separator
					emailArry = emails.Split(s);

					// if successful, stop parsing
					if (emailArry != null && emailArry.Length > 0) { break; }
				}

				if (emailArry != null && emailArry.Length > 0)
				{
					// clean email elements
					List<string> emailList = new List<string>(emailArry);
					for (int i = 0; i < emailList.Count; i++)
					{
						// remove empty element
						if (string.IsNullOrEmpty(emailList[i]))
						{
							emailList.RemoveAt(i);
							i--;
						}
						else
						{
							// trim and see if that made empty element
							emailList[i] = emailList[i].Trim();
							if (string.IsNullOrEmpty(emailList[i]))
							{
								emailList.RemoveAt(i);
								i--;
							}
						}
					}

					// convert back to array
					emailArry = emailList.ToArray();
				}
				else
				{
					// failed to parse
					Log.WriteError("dzsgfkhdfshtdstrrtsdx", "Failed to convert email string: " + emails + " into array");
				}
			}
			return emailArry;
		}


		public static bool IsKeyExistInHashtable(Hashtable h, string key)
		{
			bool isExist = false;
			if (!string.IsNullOrEmpty(key) && h[key] != null)
			{
				isExist = true;
			}

			return isExist;
		}

		/// <summary>
		/// is passed string a number?
		/// </summary>
		/// <param name="numberAsString"></param>
		/// <returns></returns>
		public static bool IsNumber(string numberAsString)
		{
			bool isNumber = false;
			if (!string.IsNullOrEmpty(numberAsString))
			{
				numberAsString = numberAsString.Trim();
				double numberTest;
				isNumber = double.TryParse(numberAsString, out numberTest);
			}
			return isNumber;
		}


		

		/// <summary>
		/// replace fist instance of string w another string
		/// http://stackoverflow.com/questions/141045/how-do-i-replace-the-first-instance-of-a-string-in-net
		/// </summary>
		/// <param name="source"></param>
		/// <param name="find"></param>
		/// <param name="replace"></param>
		/// <returns></returns>
		public static string ReplaceFirstInstance(string source, string search, string replace)
		{
			int pos = source.IndexOf(search);
			if (pos < 0)
			{
				return source;
			}
			return source.Substring(0, pos) + replace + source.Substring(pos + search.Length);
		}

		/// <summary>
		/// replace strings w/o looking at case
		/// </summary>
		/// <param name="source"></param>
		/// <param name="search">RegEx not allowed, will be escaped</param>
		/// <param name="replace"></param>
		/// <returns></returns>
		public static string ReplaceCaseInsensitive(string source, string search, string replace)
		{
			if (!string.IsNullOrEmpty(search)) { search = Regex.Escape(search); }
			return Regex.Replace(source, search, replace, RegexOptions.IgnoreCase);
		}


		/// <summary>
		/// Redirect webpage. Do not put inside a try/catch block, will through error that execution was terminated due to redirect
		/// </summary>
		/// <param name="newUrl"></param>
		/// <param name="statusCode"></param>
		public static void Redirect(string newUrl, int? statusCode)
		{
			if (HttpContext.Current != null && HttpContext.Current.Response != null)
			{
				if (statusCode == null || statusCode == 0) { statusCode = 302; }
				HttpContext.Current.Response.StatusCode = (int)statusCode;
				HttpContext.Current.Response.RedirectLocation = newUrl;
				HttpContext.Current.Response.End();
			}
		}

		public static string RemoveUrlProtocol(string url)
		{
			if (!string.IsNullOrEmpty(url))
			{
				url = url.ToLower().Trim();
				string [] protocols = new string[] { "http://", "https://", "ftp://", "file://" };
				foreach (string protocol in protocols)
				{
					url = url.Replace(protocol, "");
				}
			}
			return url;
		}

		///// <summary>
		///// Convert an absolute url to relative url
		///// If relative url passed, then no effect other than trimming and lowercasing the url
		///// </summary>
		///// <param name="url">full url</param>
		///// <param name="websiteDomainName">website FQDN, with or w/o the http protocol</param>
		///// <returns></returns>
		//public static string ConvertAbsoluteToRelativeUrl(string url, string websiteDomainName)
		//{
		//    try
		//    {
		//        if (!string.IsNullOrEmpty(url))
		//        {
		//            url = url.ToLower().Trim();
		//            url = RemoveUrlProtocol(url);

		//            if (!string.IsNullOrEmpty(websiteDomainName))
		//            {
		//                websiteDomainName = websiteDomainName.ToLower().Trim();
		//                websiteDomainName = RemoveUrlProtocol(websiteDomainName);
		//                if (websiteDomainName.Substring(websiteDomainName.Length - 1, 1) == "/")
		//                {
		//                    websiteDomainName = websiteDomainName.Substring(0, websiteDomainName.Length - 1);
		//                }
		//                if (websiteDomainName.Contains("/")) { throw new Exception("FQDN website name passed is invalid: " + websiteDomainName); }

		//                // remove FQDN from url to make it relative
		//                url = url.Replace(websiteDomainName, "");
		//            }
		//        }
		//        if (string.IsNullOrEmpty(url)) { url = "/"; }
		//    }
		//    catch (Exception ex)
		//    {
		//        SPHelper.Log.WriteError("fxhgfdhdhfsdrgsete", ex, "url: " + url + ", website FQDN: " + websiteDomainName);
		//    }
		//    return url;
		//}





		/// <summary>
		/// Count number of chars in passed string
		/// </summary>
		/// <param name="stringToSearchIn"></param>
		/// <param name="charToSearch"></param>
		/// <returns></returns>
		public static int Count(string stringToSearchIn, char charToSearch)
		{
			int count = 0;
			if (!string.IsNullOrEmpty(stringToSearchIn))
			{
				count = stringToSearchIn.Count(s => s == charToSearch);
			}
			return count;
		}

		/// <summary>
		/// Get relative and absolute url from passed sharepoint url
		/// </summary>
		/// <param name="url"></param>
		/// <param name="relativeUrl"></param>
		/// <param name="absoluteUrl"></param>
		public static void ParseSharePointUrl(string url, out string relativeUrl, out string absoluteUrl)
		{
			relativeUrl = null; absoluteUrl = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(url)) { throw new Exception("SharePoint URL passed to parse is null"); }
				bool isPassedUrlAbsolute = IsAbsoluteUrl(url);


				if (!isPassedUrlAbsolute)
				{
					// get relative url
					relativeUrl = url;
					absoluteUrl = ConvertRelativeUrlToAbsoluteUrl(relativeUrl);
				}
				else
				{
					// get absolute url
					absoluteUrl = url;
					relativeUrl = ConvertAbsoluteUrlToRelativeUrl(absoluteUrl);
				}

				// format the urls, no trailing slash, and relative url must have preceeding slash
				if (!string.IsNullOrEmpty(relativeUrl))
				{
					if (relativeUrl.Substring(0, 1) != "/") { relativeUrl = "/" + relativeUrl; }
					if (relativeUrl.Substring(relativeUrl.Length - 1) == "/")
					{ relativeUrl = relativeUrl.Substring(0, relativeUrl.Length - 1); }
				}
				if (!string.IsNullOrEmpty(absoluteUrl))
				{
					if (absoluteUrl.Substring(absoluteUrl.Length - 1) == "/")
					{ absoluteUrl = absoluteUrl.Substring(0, absoluteUrl.Length - 1); }
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("rtyuyfgdthcxcfdxfzsdsdrser", ex);
			}
		}

		public static string[] Protocols { get { return new string[] { "http://", "https://", "ftp://", "file://" }; } }

		/// <summary>
		/// Determine if passed url is absolute
		/// http://stackoverflow.com/questions/4002692/c-sharp-determine-if-absolute-or-relative-url
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		public static bool IsAbsoluteUrl(string url)
		{
			bool isAbsoluteUrl = false;
			if (!string.IsNullOrEmpty(url))
			{

				if (url.Substring(0, 1) == "/") { isAbsoluteUrl = false; }
				else
				{
					Uri result;
					isAbsoluteUrl = Uri.TryCreate(url, UriKind.Absolute, out result);
				}
			}
			return isAbsoluteUrl;
		}

		/// <summary>
		/// Convert an absolute url to relative url
		/// Removes trailing slash unless is root
		/// </summary>
		/// <param name="absoluteUrl"></param>
		/// <returns></returns>
		public static string ConvertAbsoluteUrlToRelativeUrl(string absoluteUrl)
		{
			string relativeUrl = null;
			if (!string.IsNullOrEmpty(absoluteUrl))
			{
				// confirm an absolute url and convert to relative url
				if (absoluteUrl.Substring(0, 1) == "/") { relativeUrl = absoluteUrl; }
				else
				{
					Uri uri;
					if (Uri.TryCreate(absoluteUrl, UriKind.Absolute, out uri))
					{
						relativeUrl = uri.PathAndQuery;
					}

					// remove protocol
					foreach (string protocol in Protocols)
					{
						if (relativeUrl.Length > protocol.Length && relativeUrl.Substring(0, protocol.Length) == protocol)
						{
							relativeUrl = relativeUrl.Substring(protocol.Length);
							break;
						}
					}
					if (string.IsNullOrEmpty(relativeUrl)) { relativeUrl = "/"; }

					// remove domain
					if (relativeUrl.Substring(0, 1) != "/" && relativeUrl.Contains("/"))
					{
						relativeUrl = relativeUrl.Substring(relativeUrl.IndexOf("/"));
					}

					// add preceeding slash to url to anchor to root web (if needed)
					if (string.IsNullOrEmpty(relativeUrl)) { relativeUrl = "/"; }
					if (relativeUrl.Substring(0, 1) != "/") { relativeUrl = "/" + relativeUrl; }

					// remove any trailing slash
					if (relativeUrl.Length > 1 && relativeUrl.Substring(relativeUrl.Length - 1) == "/")
					{
						relativeUrl = relativeUrl.Substring(0, relativeUrl.Length - 1);
					}
				}
			}
			return relativeUrl;
		}

		/// <summary>
		/// Returns abaolute url for passed relative url
		/// Gets absolute url for root web and combines with passed url
		/// </summary>
		/// <param name="relativeUrl">Must start with a "/" in it's url</param>
		/// <param name="site"></param>
		/// <returns></returns>
		public static string ConvertRelativeUrlToAbsoluteUrl(string relativeUrl, SPSite site)
		{
			string absoluteUrl = null;
			try
			{
				if (!string.IsNullOrEmpty(relativeUrl))
				{
					// init
					if (site == null) { site = SPHelper.Site.GetSite(); }
					if (site == null) { throw new Exception("Failed to get SPSite from SPContext"); }

					if (IsAbsoluteUrl(relativeUrl)) { absoluteUrl = relativeUrl; }
					else
					{
						// get domain url (make sure no trailing slash)
						absoluteUrl = site.RootWeb.Url;
						if (!string.IsNullOrEmpty(absoluteUrl) && absoluteUrl.Substring(absoluteUrl.Length - 1) == "/")
						{
							absoluteUrl = absoluteUrl.Substring(0, absoluteUrl.Length - 1);
						}

						// combine domain name and relative url
						absoluteUrl += relativeUrl;

						// remove any trailing slash
						if (absoluteUrl.Length > 1 && absoluteUrl.Substring(absoluteUrl.Length - 1) == "/")
						{
							absoluteUrl = absoluteUrl.Substring(0, absoluteUrl.Length - 1);
						}
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("76rrstddstrsddsd", ex, "relative url: " + relativeUrl);
				absoluteUrl = null;
			}
			return absoluteUrl;
		}

		public static string ConvertRelativeUrlToAbsoluteUrl(string relativeUrl)
		{
			// call original
			return ConvertRelativeUrlToAbsoluteUrl(relativeUrl, null);
		}

		/// <summary>
		/// Pass HttpContext and return url formatted
		/// </summary>
		/// <param name="current"></param>
		/// <param name="isRelative"></param>
		/// <param name="isExcludeQueryString"></param>
		/// <returns></returns>
		public static string FormatUrl(HttpContext current, bool isRelative, bool isExcludeQueryString)
		{
			if (current != null && current.Request != null)
			{
				// call original
				return FormatUrl(current.Request.Url, isRelative, isExcludeQueryString);
			}
			else
			{
				return null;
			}
		}
		/// <summary>
		/// Pass uri and return url formatted
		/// </summary>
		/// <param name="uri">Request.Url</param>
		/// <param name="isRelative"></param>
		/// <param name="isExcludeQueryString"></param>
		/// <returns></returns>
		public static string FormatUrl(Uri uri, bool isRelative, bool isExcludeQueryString)
		{
			var url = string.Empty;
			if (uri != null)
			{
				if (isRelative)
				{
					if (isExcludeQueryString) { url = uri.AbsolutePath; }
					else { url = uri.PathAndQuery; }
				}
				else
				{
					url = uri.ToString();
					if (isExcludeQueryString && !string.IsNullOrEmpty(url))
					{
						if (url.Contains("?")) { url = url.Substring(0, url.IndexOf("?")); }
						if (url.Contains("#")) { url = url.Substring(0, url.IndexOf("#")); }
					}
				}
			}
			return url;
		}


		/// <summary>
		/// Remove duplicate strings in a generic collection
		/// http://stackoverflow.com/questions/47752/remove-duplicates-from-a-listt-in-c-sharp
		/// </summary>
		/// <param name="list"></param>
		public static void RemoveDuplicateStrings(List<string> list)
		{
			if (list != null)
			{
				list.Sort();
				Int32 index = 0;
				while (index < list.Count - 1)
				{
					if (list[index] == list[index + 1]) { list.RemoveAt(index); }
					else { index++; }
				}
			}
		}
		/// <summary>
		/// remove duplicate string elements from an array
		/// IMPORTANT! This creates a NEW array object, therefore you must re-assign it
		/// back to the originally passed array
		/// </summary>
		/// <param name="a"></param>
		/// <returns></returns>
		public static string[] RemoveDuplicateStrings(string[] a)
		{
			string[] newArray = null;
			try
			{
				// init
				if (a == null) { throw new Exception("Array passed is null"); }

				List<string> noDups = new List<string>();
				for (int i = 0; i < a.Length; i++)
				{
					if (!noDups.Contains(a[i])) { noDups.Add(a[i]); }
				}
				newArray = noDups.ToArray();
			}
			catch (Exception ex)
			{
				Log.WriteError("ey5drtsrJJyesyer", ex);
				newArray = null;
			}
			return newArray;
		}

		/// <summary>
		/// remove duplicate objects.
		/// </summary>
		/// <param name="list"></param>
		public static void RemoveDuplicates<T>(List<T> list)
		{
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					bool isMatch = false;
					for (int ii = 0; ii < list.Count; ii++)
					{
						if (i != ii && (object)list[i] == (object)list[ii])
						{
							isMatch = true;
							break;
						}
					}
					if (isMatch)
					{
						list.RemoveAt(i);
						i--;
					}
				}
			}
		}

		/// <summary>
		/// divide an amount evenly
		/// http://stackoverflow.com/questions/577427/evenly-divide-in-c-sharp
		/// </summary>
		/// <param name="numerator">The amount to divide and distribute</param>
		/// <param name="denominator">The number of times to divide the amount by</param>
		/// <returns></returns>
		private static IEnumerable<int> divideEvenly(int numerator, int denominator)
		{
			int rem;
			int div = Math.DivRem(numerator, denominator, out rem);

			for (int i = 0; i < denominator; i++)
			{
				yield return i < rem ? div + 1 : div;
			}
		}

		/// <summary>
		/// Divide amount (numerator) evenly by denominator, with any remainder also distributed
		/// For example, if divide 100 by 3, return int[0] = 34, int[1] = 33, int[2] = 33
		/// The first element has the remainder added (100 - (33 * 3) = 1)
		/// </summary>
		/// <param name="numerator"></param>
		/// <param name="denominator"></param>
		/// <returns></returns>
		public static List<int> DivideEvenly(int numerator, int denominator)
		{
			var results = divideEvenly(numerator, denominator);
			return (results != null) ? results.Cast<int>().ToList() : new List<int>();
		}



		private static Random random = new Random();
		public static int GetRandomNumber(int min, int max)
		{
			int r = 0;
			try
			{
				if (min >= max) { throw new Exception("Min value is greater than or equal to the max value"); }
				r = random.Next(min, max);
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("Drsgdfydsrrsd", ex, "min: " + min + " max: " + max);
			}
			return r;
		}

		/// <summary>
		/// Repeat string
		/// http://stackoverflow.com/questions/532892/can-i-multiply-a-string-in-c
		/// </summary>
		/// <param name="source"></param>
		/// <param name="multiplier"></param>
		/// <returns></returns>
		public static string Multiply(string source, int multiplier)
		{
			StringBuilder sb = new StringBuilder(multiplier * source.Length);
			for (int i = 0; i < multiplier; i++)
			{
				sb.Append(source);
			}
			return sb.ToString();
		}

		/// <summary>
		/// Repeat string
		/// http://stackoverflow.com/questions/532892/can-i-multiply-a-string-in-c
		/// </summary>
		/// <param name="source"></param>
		/// <param name="multiplier"></param>
		/// <returns></returns>
		public static string Repeat(string source, int multiplier)
		{
			 // call original
			return Multiply(source, multiplier);
		}


	} // utilities class
}
